#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include "tds_select.h"

void mssql_select_clean(MSSQL_CONN* conn)
{
    int i,j;
    for(i = 0; i < conn->RSTnROW; i++)
        for(j = 0; j < conn->RSTnCOL; j++)
        {
            if(conn->RSTSET[i*conn->RSTnCOL+j] != NULL)
                free(conn->RSTSET[i*conn->RSTnCOL+j]);
        }
    if(conn->RSTSET != NULL)
        free(conn->RSTSET);
}

void mssql_select_print(MSSQL_CONN* conn)
{
    int i,j;
    for(i = 0; i < conn->RSTnROW; i++){
        for(j = 0; j < conn->RSTnCOL; j++)
        {
            if(conn->RSTSET[i*conn->RSTnCOL+j] != NULL)
                fprintf(stdout, "%*s", 20, conn->RSTSET[i*conn->RSTnCOL+j]);
        }
        printf("\n");
    }
}

/**
  * @brief  mssql 'select'查询语句处理流程
  * @Notes  None.
  * @param  conn, mssql连接实例
  * @retval 状态值(-1 ~ -10) 或者 处理完成的数据条目数量(Row行数)
  */
int mssql_select(MSSQL_CONN* conn)
{
    RETCODE erc;
    while((erc = dbresults(conn->dbprocess)) != NO_MORE_RESULTS) {
        int ncols; int row_code; 
        struct col {        //保存列的所有信息
            char *name;     //列名字
            char *buffer;   //存放列数据指针
            int type, size, status;
        } *columns, *pcol;
        
        if(erc == FAIL) {
            fprintf(stderr, "TDS/> [dbresults] failed.\n");
            return SELECT_DBRESULT_FAIL;  
        } 
        //返回执行结果的列数目
        ncols = dbnumcols(conn->dbprocess); 
        //(全)列数据、属性存储空间
        if((columns = calloc(ncols, sizeof(struct col))) == NULL) {
            perror(NULL);
            fprintf(stderr, "SYS/> ALL-Column fields space allocated failed.\n");
            return SELECT_ALLOC_ALLCOL_FIELDS_FAIL; 
        }
        /* 结果集：字段数据(字符串指针格式)
         * 第一次使用malloc分配一行(ncols个)字符串指针的空间
         * 后续使用realloc逐行追加ncols个字符串指针空间
         */
        conn->RSTSET = (char**)malloc(sizeof(char*) * ncols);
        if(conn->RSTSET == NULL)
        {
            fprintf(stderr, "SYS/> Result-Set memory space allocated failed.\n");
            return SELECT_ALLOC_RSTSET_FAIL;
        }
        //行索引辅助变量
        int RSTiROW = 0;
        //保存结果集列数
        conn->RSTnCOL = ncols;
        /* read metadata and bind. */
        for(pcol = columns; pcol - columns < ncols; pcol++) {
            int c = pcol - columns + 1;
            pcol->name = dbcolname(conn->dbprocess, c); //返回指定列的列名
            pcol->type = dbcoltype(conn->dbprocess, c); //列字段数据类型
            pcol->size = dbcollen(conn->dbprocess, c);  //列字段数据在建表时的存储长度
            //printf("%*s(%d)", 20, pcol->name, pcol->size);        //显示列名称及字段长度
            if((pcol->buffer = calloc(1, pcol->size)) == NULL) {    //创建列字段数据缓冲区
                perror(NULL);
                fprintf(stderr, "SYS/> Single-field shared buffer allocated failed.\n");
                return SELECT_ALLOC_FIELDS_SHRBUF_FAIL;  
            }
            /* Parameters
             * dbproc  contains all information needed by db-lib to manage communications with the server.
             * column  Nth column, starting at 1.
             * vartype datatype of the host variable that will receive the data
             * varlen  size of host variable pointed to varaddr
             * varaddr address of host variable
             */
            if((erc = dbbind(conn->dbprocess, c, NTBSTRINGBIND, pcol->size, (BYTE*)pcol->buffer)) == FAIL) {
                fprintf(stderr, "TDS/> Single-field shared buffer dbbind(%d) failed\n", c);
                return SELECT_BIND_FIELDS_SHRBUF_FAIL;  
            }  

            /*  Return values
             *  0 column bound successfully
             * -1 column is NULL.
             * >0 true length of data, had column not been truncated due to insufficient space in the columns bound host variable .
             */
            if(( erc = dbnullbind(conn->dbprocess, c, &pcol->status)) == FAIL) {
                fprintf(stderr, "TDS/> Signle-field shared status-word dbnullbind(%d) failed\n", c);
                return SELECT_NULLBIND_FIELDS_STATUS_FAIL;  
            }
            //列名称保存到结果集
            conn->RSTSET[RSTiROW*ncols + c -1] = (char*)malloc(sizeof(char)*pcol->size);
            if(conn->RSTSET[RSTiROW*ncols + c -1] == NULL)
            {
                fprintf(stderr, "SYS/> Column-Name field buffer allocated failed.\n");
                return SELECT_ALLOC_COLNAME_FAIL;
            }
            sprintf(conn->RSTSET[RSTiROW*ncols + c -1], "%s(%d)", pcol->name, pcol->size);
            //printf("%s\n", conn->RSTSET[RSTiROW*ncols + c -1]);
        }
        //第一行(列名称)结束，行索引加1
        RSTiROW = RSTiROW + 1; 
        /* 获取每一行所有数据字段指针占用的空间
         * 注意：使用正确的数据类型做单位长度
         */
        int RST_ROW_SIZE = ncols * sizeof(char*);

        /* 打印/读取 每行数据 */
        int c; char **NEW_RSTSET;
        while((row_code = dbnextrow(conn->dbprocess)) != NO_MORE_ROWS) {//逐行读取数据条目
            switch(row_code) {
                case REG_ROW:
                    //扩展新数据行存储空间 +1, 空间扩展后的所有数据行数量
                    NEW_RSTSET = (char**)realloc(conn->RSTSET, RST_ROW_SIZE*(RSTiROW + 1) );
                    if(NEW_RSTSET != NULL){
                        conn->RSTSET = NEW_RSTSET;
                    }else{
                        fprintf(stderr, "SYS/> Result-Set memory space [re]allocated failed in [dbnextrow] procedure.\n");
                        return SELECT_DBNEXTROW_REALLOC_RSTSET_FAIL;
                    }
                    //逐列写入到结果集缓冲区
                    for(pcol=columns, c = 0; pcol - columns < ncols; pcol++, c++) {
                        //判断field结构体中的状态值是否为 -1(failed)
                        char *buffer = pcol->status == -1 ? "null" : pcol->buffer;
                        //显示字段数据
                        //printf("%*s ", 20, buffer);
                        //获取字段数据并存储到结果集缓冲区
                        conn->RSTSET[RSTiROW*ncols + c] = (char*)malloc(sizeof(char)*pcol->size);
                        if(conn->RSTSET[RSTiROW*ncols + c] == NULL)
                        {
                            fprintf(stderr, "SYS/> Column-Data field buffer allocated failed in [dbnextrow] procedure.\n");
                            return SELECT_DBNEXTROW_ALLOC_FIELD_FAIL;
                        }
                        sprintf(conn->RSTSET[RSTiROW*ncols + c], "%s", buffer);
                    }
                    //行索引加1
                    RSTiROW = RSTiROW + 1;
                    break;
                case BUF_FULL: 
                    break;
                case FAIL:
                    fprintf(stderr, "TDS/> [dbnextrow] failed.\n");
                    return SELECT_DBNEXTROW_FAIL;
                    break;
                default:
                    printf("data for computeid %d ignored.\n", row_code);
            }
        }
        //保存行数值
        conn->RSTnROW = RSTiROW;
        /* free metadata and data buffers */
        for(pcol=columns; pcol - columns < ncols; pcol++) {
            free(pcol->buffer);
        }
        free(columns);
        /* 得到SQL语句影响的行数，并返回 */
        if(DBCOUNT(conn->dbprocess) > -1) {
            return DBCOUNT(conn->dbprocess);
        }
    }
}
